#include <cmath>

#include "PlayerController.h"
#include "Character.h"
#include "ResourceDatabase.h"

#define PI 3.14159265

PlayerController::PlayerController()
: responsive(false)
, switchingLevels(false)
{
}

PlayerController::PlayerController(PlayerPtr player, CameraPtr camera, const std::string& playerDataFn)
: player(player)
, responsive(true)
, switchingLevels(false)
, gravity(1, 50)
, actualGravity(0, 0)
, hand(IMG_HAND)
, camera(camera)
//, currentLevel(NULL)
{
    hand.SetOrigin(10, 10);
    //readFile(playerDataFn);
}

void PlayerController::update(float delta)
{
    if(switchingLevels)
    {
        return;
    }

    //mouseClickDelay.update(delta);
    player->getSprite()->update(delta);

    /*if (player->getPosition().y < 400 ) // this if statement is only tmp.
    {
        actualGravity.x += gravity.x * delta;
        actualGravity.y += gravity.y * delta;
    }
    else
    {
        actualGravity.x = 0;
        actualGravity.y = 0;
    }
    
    */
    sf::Vector2f velocity = player->getVelocity(); 
    if(velocity.x > 0)
    {
        velocity.x -= gravity.x;
        if(velocity.x < 0)
            velocity.x = 0;
    }
    else if(velocity.x < 0)
    {
        velocity.x += gravity.x;
        if(velocity.x > 0)
            velocity.x = 0;
    }
    else if(velocity.y > 0)
    {
        velocity.y -= gravity.y;
        if(velocity.y < 0)
            velocity.y = 0;
    }
    else if(velocity.y < 0)
    {
        velocity.y += gravity.y;
        if(velocity.y > 0)
            velocity.y = 0;
    }

    //actualGravity -= gravity;

    player->setVelocity(velocity);
    if (!((player->getVelocity().x == 0) && (player->getVelocity().y == 0)))
    {
        player->move(player->getVelocity() * delta);
        camera->characterMove(player->getVelocity() * delta, sf::IntRect((sf::Vector2i)player->getPosition(),(sf::Vector2i)player->getSprite()->GetSize()));
    }

    //update weapon object
    weapon.update(delta);
}

void PlayerController::update(const sf::RenderWindow& window)
{
    float delta = window.GetFrameTime() / 1000.0f;
    const static float speed = player->getSpeed();

    const sf::Input& input = window.GetInput();

    if(input.IsKeyDown(sf::Key::D) || input.IsKeyDown(sf::Key::Right))
        player->setVelocity(sf::Vector2f(speed, 0));
    if(input.IsKeyDown(sf::Key::A) || input.IsKeyDown(sf::Key::Left))
        player->setVelocity(sf::Vector2f(-speed, 0));
    if(input.IsKeyDown(sf::Key::W) || input.IsKeyDown(sf::Key::Up))
        player->setVelocity(sf::Vector2f(0, -speed));
    if(input.IsKeyDown(sf::Key::S) || input.IsKeyDown(sf::Key::Down))
        player->setVelocity(sf::Vector2f(0, speed));

    //if(input.IsKeyDown(sf::Key::Space))
    //    wasSpaceDown = true;
    //if(!input.IsKeyDown(sf::Key::Space) && wasSpaceDown)
    //{  // Stopped pressing space bar.
    //    wasSpaceDown = false;
    //    if (actualGravity.x == 0 && actualGravity.y == 0)
    //    {
    //        jump();
    //    }
    //}

    sf::Vector2i mousePos
        ( input.GetMouseX() + camera->getView().GetCenter().x - camera->getView().GetSize().x / 2
        , input.GetMouseY() + camera->getView().GetCenter().y - camera->getView().GetSize().y / 2);

    if (input.IsMouseButtonDown(sf::Mouse::Left))
        fireSelectedWeapon(mousePos);
        
    hand.SetRotation(-180 + (atan2((double)hand.GetPosition().y - mousePos.y,
        (double)hand.GetPosition().x - mousePos.x) * 180 / PI));
    hand.update(0);
    //if(!currentLevel || switchingLevels) return;
    //
    //if(responsive)
    //{
    //    if(event.IsKeyDown == sf::Event::KeyPressed)
	   // { // We're not in the middle of changing levels; can interact with player.
    //        sf::Key::Code keyPressed = event.Key.Code;
    //        if(!DialogueManager::Get().Is_Dialogue())
    //        { // No dialogue blocking input.
    //        }
    //        else
    //        { // Dialogue open.
    //            if(keyPressed == sf::Key::Up)
    //            {
    //                DialogueManager::Get().scrollDialogueUp();
    //            }
    //            else if(keyPressed == sf::Key::Down)
    //            {
    //                DialogueManager::Get().scrollDialogueDown();
    //            }
    //            else if(keyPressed == sf::Key::Space)
    //            {
    //                DialogueManager::Get().popDialogue();
    //            }
    //        }
    //        lastMousePos = MOUSE;
    //    }
    //    else if(0/*event.type == mouse wheel up*/)
    //    {
    //        scrollSelectedWeapon(true);
    //    }
    //    else if(0/*event.type == mouse wheel down*/)
    //    {
    //        scrollSelectedWeapon(false);
    //    }
    //}
}

//void PlayerController::onStarted()
//{
//    responsive = false;
//    switchingLevels = true;
//    //player_dest = sf::Vector2i();
//    player->Set_Current_Action(action_ptr());
//    //player->Finish_Walk();
//
//    player->Clear_Active_Spell();
//}
//
//void PlayerController::onIntermission(bool isFirstLoaded, Level& newLevel, const sf::Vector2i& entryPoint)
//{
//    player->Set_Position(entryPoint);
//    currentLevel = &newLevel;
//}
//
//void PlayerController::onFinished()
//{    
//    responsive = true;
//    switchingLevels = false;
//}

//void PlayerController::readFile(const boost_path& fileName)
//{
//}
//
//void PlayerController::writeFile(const boost_path& fileName) const
//{
//}

void PlayerController::fireSelectedWeapon(const sf::Vector2i& targetPos)
{
    weapon.shoot(player->getPosition(),targetPos);
}

//void PlayerController::pickupItem(Inventory& sourceInv, const ItemStack& item)
//{
//    // Remove however many items we can from sourceInv and add them to our inventory.
//    if(player->getInventory().Add(item.getItem(), sourceInv.Remove(item)) > 0)
//    {
//        playSound(PICKED_UP_ITEM);
//    }
//}
//
//void PlayerController::dropItem(const ItemStack& item)
//{ // Drop item at feet.
//    currentLevel->addItem(item, player->Get_Position());
//    player->getInventory().remove(item);
//    playSound(DROPPED_ITEM);
//}

//bool PlayerController::isEquipped(const Item& item) const
//{
//    return player->getSlots().isHeld(item);
//}
//
//optional_item PlayerController::equipItem(const Item& item)
//{
//    ITEM_TYPE item_type = GET_ITEM_DATA(item).getType();
//    optional_item removed = player->getSlots().useSlot(item_type, item);
//    // We no longer have the equipped item in our inventory because we're using it.
//    player->getInventory().Remove(item, 1);
//    if(removed)
//    { // Add unequipped item back into inventory.
//        player->getInventory().Add(*removed, 1);
//    }
//    playSound(EQUIPPED_ITEM);
//    return removed;
//}
//
//void PlayerController::unequipItem(const Item& item)
//{
//    ITEM_TYPE item_type = GET_ITEM_DATA(item).getType();
//    if(player->getSlots().Is_Slot_Used(item_type))
//    {
//        player->getSlots().Clear_Slot(item_type);
//        player->getInventory().Add(item, 1);
//        playSound(UNEQUIPPED_ITEM);
//    }
//    else
//    { // Should never happen.
//        throw std::runtime_error("Tried to unequip an item that wasn't equipped.");
//    }
//}

//void PlayerController::playSound(PLAYER_SOUND sound)
//{
//    SoundIt it = playerSounds.find(sound);
//
//    if(it != playerSounds.end())
//    {
//        //audioManager.playSound(it->second);
//    }
//}

//PlayerPtr PlayerController::getPlayer()
//{
//    return player;
//}
//
//void PlayerController::setPlayer(PlayerPtr player)
//{
//    this->player = player;
//
//    //Read(playerDataFn);
//}

void PlayerController::setSelectedWeapon(/*weapon*/)
{
}

void PlayerController::scrollSelectedWeapon(bool up)
{
    //if(player->getInventory().isEmpty())
    //{ // No spells to scroll over.
    //    return;
    //}

    //size_t selectedWeaponIndex = player->getInventory().getIndexOf(player->getSelectedWeaponId());

    //if(up)
    //{
    //    if(selectedWeaponIndex == 0)
    //    {
    //        selectedWeaponIndex = player->getInventory().Get_Size() - 1;
    //    }
    //    else
    //    {
    //        --selectedWeaponIndex;
    //    }
    //}
    //else
    //{
    //    if(selectedWeaponIndex == player->getInventory().Get_Size() - 1)
    //    {
    //        selectedWeaponIndex = 0;
    //    }
    //    else
    //    {
    //        ++selectedWeaponIndex;
    //    }
    //}

    //player->setSelectedWeaponId(player->getInventory().getInventory()[selectedWeaponIndex].Get_ID());
}

void PlayerController::jump()
{
    actualGravity -= gravity;
    player->move(sf::Vector2f(0, -0.1f));//ONLY TEMP
}

void PlayerController::draw(  sf::RenderWindow& window )
{
    window.Draw(*player->getSprite());
    hand.SetPosition(player->getPosition());
    hand.Move(sf::Vector2f(60,60));
    window.Draw(hand);
}

std::istream& operator>>(std::istream& stream, PlayerController::PLAYER_SOUND& type)
{
    std::string buff;
    stream >> buff;

    // Can't use enum tools since we have to qualify the enum.
    if(buff == "PICKED_UP_ITEM") type = PlayerController::PICKED_UP_ITEM;
    else if(buff == "DROPPED_ITEM") type = PlayerController::DROPPED_ITEM;
    else if(buff == "EQUIPPED_ITEM") type = PlayerController::EQUIPPED_ITEM;
    else if(buff == "UNEQUIPPED_ITEM") type = PlayerController::UNEQUIPPED_ITEM;
    else
    {
        stream.setstate(std::ios_base::badbit);
    }

    return stream;
}

std::ostream& operator<<(std::ostream& stream, const PlayerController::PLAYER_SOUND& type)
{
    std::string buff;

    if(type == PlayerController::PICKED_UP_ITEM) buff = "PICKED_UP_ITEM";
    else if(type == PlayerController::DROPPED_ITEM) buff = "DROPPED_ITEM";
    else if(type == PlayerController::EQUIPPED_ITEM) buff = "EQUIPPED_ITEM";
    else if(type == PlayerController::UNEQUIPPED_ITEM) buff = "UNEQUIPPED_ITEM";
    else
    {
        stream.setstate(std::ios_base::badbit);
    }

    if(stream) stream << buff;
    return stream;
}